//
// Created by martin on 9/28/21.
//

#include "ef_string.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>

struct ef_string* ef_string_new()
{
    struct ef_string *s = NULL;
    s = (struct ef_string *)malloc(sizeof(struct ef_string));
    if (!s) {
        goto errout;
    }

    s->len = 0;
    s->capacity = EF_STRING_CAPACITY_INIT;
    s->str = (const char *) malloc(sizeof(char) * s->capacity);
    if (!s->str) {
        goto errout;
    }

    s->factor = EF_STRING_FACTOR_DEFAULT;
    assert(s->factor > 1);
    return s;

    errout:
    free(s);
    return NULL;
}

struct ef_string* ef_string_new_with_string(const char *str)
{
    struct ef_string *s = ef_string_new();
    s = ef_string_append(s, str);
    return s;
}

void ef_string_free(struct ef_string **s)
{
    if (!s || !(*s)) return;

    free((*s)->str);
    *s = NULL;
}

int ef_string_get_length(struct ef_string *s)
{
    if (!s) return -1;

    return s->len;
}

static int resize(struct ef_string *s, int increase_len) {
    assert(s->factor > 1);
    if (!s) return -1;
    int new_capacity = s->capacity * s->factor;

    while (new_capacity < s->capacity + increase_len) {
        new_capacity = new_capacity * s->factor;
    }

    char *new_str = (char *) malloc(sizeof(char) * new_capacity);
    if (!new_str) { // malloc error: memory is not enough
        return -1;
    }

    strcpy(new_str, s->str);
    free(s->str);
    s->str = new_str;
    s->capacity = new_capacity;

    return s->capacity;
}

struct ef_string* ef_string_append(struct ef_string *s, const char *str)
{
    if (!s || !str) return s;

    int length = strlen(str);
    if (s->len + length + 1 > s->capacity) { // +1 for null byte('\0')
        if (resize(s, length) < 0) { // error: dont change the capacity of s
            return s;
        }
    }

    strcpy(s->str + s->len, str);
    s->len += length;
    assert(s->len <= s->capacity);

    s->str[s->len] = 0;
    return s;
}

const char *ef_string_cstr(struct ef_string *s)
{
    if (!s) return NULL;

    return s->str;
}